/**
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.api.adwords.starterkit.Synchronizer;

import com.google.api.adwords.starterkit.settings.StarterKitSettings;
import com.google.api.adwords.starterkit.utils.DumpExceptions;
import com.google.api.adwords.starterkit.utils.ServiceUtils;

import org.apache.log4j.Logger;

import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * The Class AccountSynchronizer is the entry point for the synchronization
 * process. The synchronization process is responsible to keep AdWords system
 * synchronized with local DB.
 * 
 * @author rodrigo.f.vale@gmail.com (Rodrigo Vale)
 */
public class AccountSynchronizer {
  private static final Logger log = Logger.getLogger("StarterKit");
  private StarterKitSettings starterKitSetting;
  private ServiceUtils serviceUtils;
  private Connection conn = null;
  private CampaignSynchronizer campaignSynchronizer;
  private NegativeKeywordsSynchronizer negativeKeywordsSynchronizer; 
  private AdGroupSynchronizer adGroupSynchronizer;
  private KeywordSynchronizer keywordSynchronizer;
  private TextAdSynchronizer textAdSynchronizer;
  private TargetsSynchronizer targetsSynchronizer;
  private GeoTargetSynchronizer geoTargetSynchronizer;
  private LanguageTargetSynchronizer languageTargetSynchronizer;
  private NetworkTargetSynchronizer networkTargetSynchronizer;
  private PlatformTargetSynchronizer platformTargetSynchronizer;
  
  public AccountSynchronizer(StarterKitSettings starterKitSetting, 
      Connection conn) {
    this.conn = conn;
    this.setServiceUtils(new ServiceUtils(starterKitSetting));
    this.starterKitSetting = starterKitSetting;
    
    File cache = new File(starterKitSetting.getCacheDir());
    if (!cache.mkdirs()) {
      log.error("Failed to create cache directory at "
          + starterKitSetting.getCacheDir());
    }

    campaignSynchronizer = new CampaignSynchronizer(starterKitSetting, conn);
    negativeKeywordsSynchronizer 
        = new NegativeKeywordsSynchronizer(starterKitSetting, conn);
    adGroupSynchronizer = new AdGroupSynchronizer(starterKitSetting, conn);
    keywordSynchronizer = new KeywordSynchronizer(starterKitSetting, conn);  
    textAdSynchronizer = new TextAdSynchronizer(starterKitSetting, conn);  

    targetsSynchronizer = new TargetsSynchronizer(starterKitSetting, conn);
    geoTargetSynchronizer = new GeoTargetSynchronizer(starterKitSetting, conn);
    languageTargetSynchronizer 
        = new LanguageTargetSynchronizer(starterKitSetting, conn);
    networkTargetSynchronizer 
        = new NetworkTargetSynchronizer(starterKitSetting, conn);
    platformTargetSynchronizer 
        = new PlatformTargetSynchronizer(starterKitSetting, conn);
  }

  /**
   * Save all updates from db to AdWords.
   */
  public void put() {
    try {
      conn.setAutoCommit(false);    
    } catch (SQLException e) {
      DumpExceptions.dumpSQLException(e);      
    }    
    // For manualCPM StarterKit should first update NetWorkTarget of old
    // campaigns to avoid errors during Campaign update step.
    if (starterKitSetting.getCampaignSettings().getBiddingStrategy().equals(
        "manualcpm")) {
      networkTargetSynchronizer.put();
    }
    campaignSynchronizer.put();

    negativeKeywordsSynchronizer.put();
    
    geoTargetSynchronizer.put();
    languageTargetSynchronizer.put();
    networkTargetSynchronizer.put();
    platformTargetSynchronizer.put();
    
    adGroupSynchronizer.put();
    keywordSynchronizer.put();
    keywordSynchronizer.putKeywordParams();
    textAdSynchronizer.put();

    try {
      conn.commit();
      conn.setAutoCommit(true);    
    } catch (SQLException e) {
      DumpExceptions.dumpSQLException(e);      
    }    
    log.debug("Finneshed to save AdWords information");
  }

  /**
   * Load all AdWords account data to DB.
   * 
   * @param syncType
   *          the sync type
   */
  public void get(SynchronizerType syncType) {
    log.debug("Started to load AdWords Information");
    Statement stat;
    
    try {
      conn.setAutoCommit(false);    
    } catch (SQLException e) {
      DumpExceptions.dumpSQLException(e);      
    }
    
    if (syncType == SynchronizerType.DEEP) {
      log.debug("Deleting DB content");
      try {
        stat = conn.createStatement();
        stat.executeUpdate("DELETE FROM Campaigns;");
        stat.executeUpdate("DELETE FROM TextAds;");
        stat.executeUpdate("DELETE FROM Keywords;");
        stat.executeUpdate("DELETE FROM KeywordParams;");
        stat.executeUpdate("DELETE FROM AdGroups;");
        stat.executeUpdate("DELETE FROM negativekeywords;");

        stat.executeUpdate("DELETE FROM NetworkTargets;");
        stat.executeUpdate("DELETE FROM GeoTargets;");
        stat.executeUpdate("DELETE FROM LanguageTargets;");
        stat.executeUpdate("DELETE FROM PlatformTargets;");
      } catch (SQLException e) {
        DumpExceptions.dumpSQLException(e);
      }
    }
    
    if (syncType != SynchronizerType.NO) {
      campaignSynchronizer.get();
      negativeKeywordsSynchronizer.get();
      // all targets can be got in a single CampaignTargetSelector call
      targetsSynchronizer.get();
      adGroupSynchronizer.get();
      keywordSynchronizer.get();      
      textAdSynchronizer.get();
      
      // the load stats should be processed after loading all AdWords
      // information. Also, the Stats call should follow this specific 
      // order.
      // loads kwds stats from AdWords using reports
      keywordSynchronizer.getStats();
      // Based on Kwds stats, process the AdGroup Stats
      adGroupSynchronizer.getStats();
      // Based on AdGroup stats, process the Campaign Stats
      campaignSynchronizer.getStats();
    }
    
    // in the case of a SEVERE synchronization, all flags should be set
    // to its default values
    if (syncType == SynchronizerType.DEEP) {
      campaignSynchronizer.campaignTable.reset();
      adGroupSynchronizer.adGroupTable.reset();
      keywordSynchronizer.keywordTable.reset();
      textAdSynchronizer.textAdTable.reset();
    }    
    
    try {
      conn.commit();
      conn.setAutoCommit(true);    
    } catch (SQLException e) {
      DumpExceptions.dumpSQLException(e);      
    }
    log.debug("Finneshed to load AdWords Information");
  }

  public void setServiceUtils(ServiceUtils serviceUtils) {
    this.serviceUtils = serviceUtils;
  }

  public ServiceUtils getServiceUtils() {
    return serviceUtils;
  }
}
